﻿/*
Copyright 2012 DaCoder
This file is part of daCoders Tool for WoW 1.12.1.
daCoders Tool for WoW 1.12.1 is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
daCoders Tool for WoW 1.12.1 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with Foobar. If not, see http://www.gnu.org/licenses/.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Magic;

namespace OpenHack
{
    public class GameReader
    {
        // declare information about Wow and the addresses, offsets etc
        // that we will be using

        static uint BaseAddress;
        uint StaticClientConnection;
        uint CurrentTargetGUID;

        uint ClientConnection = 0;      

        uint FirstObjectOffset = (uint)Pointers.ObjectManager.FirstObject;
        uint FirstObject = 0;                

        // declare some other information
        uint TotalWowObjects = 0;

        public void init()
        {
            BaseAddress = Offsets.WoW.baseAddress;
            StaticClientConnection = BaseAddress + (uint)Pointers.ObjectManager.CurMgrPointer;
            CurrentTargetGUID = BaseAddress + (uint)Pointers.StaticPointers.CurrentTargetGUID;
        }

        /*
        public void initHacks()
        {      
            // Thx to JADD for this Offsets
            WowReader.WriteFloat((uint)0x0080DFFC, (float)0.0); // Max Wallclimb angle
            WowReader.WriteUInt((uint)0x007C63DA, (uint)0x8990c931); // Enable NoFallDMG (To disable set to 0x89784F8B)
        }
     */

        private string ReadASCIIString(uint addr)
        {
            return WowReader.ReadASCIIString(addr, 50);
        }


        // declare wow reader (which will read information about wow)
        BlackMagic WowReader = Globals.Magic;


        // declare the wowobjects we will be using. current object (to loop through the object manager),
        // player object (to provide information about our player), target object (to provide
        // information about our target)
        WowObject LocalPlayer = new WowObject();
        WowObject LocalTarget = new WowObject();
        WowObject CurrentObject = new WowObject();

        public string ItemNameFromGuid(ulong Guid)
        {
            uint ObjectBase = GetObjectBaseByGuid(Guid);
            return ReadASCIIString((WowReader.ReadUInt((WowReader.ReadUInt((ObjectBase + (uint)Pointers.UnitName.ObjectName1)) + (uint)Pointers.UnitName.ObjectName2))));
        }

        public string ItemNameFromBaseAddr(uint BaseAddr)
        {
            uint ObjectBase = BaseAddr;
            return ReadASCIIString((WowReader.ReadUInt((WowReader.ReadUInt((ObjectBase + (uint)Pointers.UnitName.ObjectName1)) + (uint)Pointers.UnitName.ObjectName2))));
        }

        public uint ItemTypeFromBaseAddr(uint BaseAddr)
        {
            uint ObjectBase = BaseAddr;
            return WowReader.ReadUInt((ObjectBase + (uint)Pointers.UnitName.ItemType));
        }

        public string MobNameFromGuid(ulong Guid)
        {
            uint ObjectBase = GetObjectBaseByGuid(Guid);
            return ReadASCIIString((WowReader.ReadUInt((WowReader.ReadUInt((ObjectBase + (uint)Pointers.UnitName.UnitName1)) + (uint)Pointers.UnitName.UnitName2))));
        }

        public string MobNameFromBaseAddr(uint BaseAddr)
        {
            uint ObjectBase = BaseAddr;
            return ReadASCIIString((WowReader.ReadUInt((WowReader.ReadUInt((ObjectBase + (uint)Pointers.UnitName.UnitName1)) + (uint)Pointers.UnitName.UnitName2))));
        }

 
        public string PlayerNameFromGuid(ulong guid)        
        {
            ulong nameStorePtr = BaseAddress + (uint)Pointers.UnitName.PlayerNameCachePointer; // Player name database
            ulong base_, testGUID;

            base_ = WowReader.ReadUInt((uint)nameStorePtr);
            testGUID = WowReader.ReadUInt64(((uint)base_ + (uint)Pointers.UnitName.PlayerNameGUIDOffset));

            while (testGUID != guid)
            {
                //read next
                base_ = WowReader.ReadUInt(((uint)base_));
                testGUID = WowReader.ReadUInt64(((uint)base_ + (uint)Pointers.UnitName.PlayerNameGUIDOffset));
            }

            // Hopefully found the guid in the name list...
            // I don't know how to check for not found
            return ReadASCIIString((uint)base_ + (uint)Pointers.UnitName.PlayerNameStringOffset);
        }

        private uint GetObjectBaseByGuid(ulong Guid)
        {
            WowObject TempObject = new WowObject();
            // set the current object to the first object in the object manager
            TempObject.BaseAddress = FirstObject;

            // while the base address of the current object is not 0, find the guid
            // and compare it to the one passed in. if it matches, return that base
            // address, otherwise continue looking
            while (TempObject.BaseAddress != 0)
            {
                try
                {
                    TempObject.Guid = WowReader.ReadUInt64((TempObject.BaseAddress + (uint)Pointers.WowObject.Guid));
                    if (TempObject.Guid == Guid)
                        return TempObject.BaseAddress;
                    TempObject.BaseAddress = WowReader.ReadUInt((TempObject.BaseAddress + (uint)Pointers.ObjectManager.NextObject));
                }
                catch (Exception)
                {
                    return 0;
                }

            }
            // if we reached here it means we couldn't find the Guid we were looking for, return 0
            return 0;
        }

        private ulong GetObjectGuidByBase(uint Base)
        {
            return WowReader.ReadUInt64((Base + (uint)(uint)Pointers.WowObject.Guid));
        }

        public Boolean LoadAddresses()
        {            
            ClientConnection = WowReader.ReadUInt((StaticClientConnection));
            FirstObject = WowReader.ReadUInt((ClientConnection + FirstObjectOffset));

            LocalTarget.Guid = WowReader.ReadUInt64(BaseAddress + (uint)Pointers.StaticPointers.CurrentTargetGUID);
            LocalPlayer.Guid = WowReader.ReadUInt64(BaseAddress + (uint)Pointers.StaticPointers.LocalPlayerGUID);

            // if the local guid is zero it means that something failed.
            if (LocalPlayer.Guid == 0)
                return false;
            else
                return true;
        }


        public WowObject getLocalPlayer()
        {
            LocalPlayer.BaseAddress = GetObjectBaseByGuid(LocalPlayer.Guid);
            LocalPlayer.XPos = WowReader.ReadFloat((LocalPlayer.BaseAddress + (uint)Pointers.WowObject.X)); 
            LocalPlayer.YPos = WowReader.ReadFloat((LocalPlayer.BaseAddress + (uint)Pointers.WowObject.Y)); 
            LocalPlayer.ZPos = WowReader.ReadFloat((LocalPlayer.BaseAddress + (uint)Pointers.WowObject.Z));
            LocalPlayer.Facing = WowReader.ReadFloat((LocalPlayer.BaseAddress + (uint)Pointers.WowObject.FacingAngle)); 
            LocalPlayer.Rotation = WowReader.ReadFloat((LocalPlayer.BaseAddress + (uint)Pointers.WowObject.RotationOffset)); 
            LocalPlayer.UnitFieldsAddress = WowReader.ReadUInt((LocalPlayer.BaseAddress + (uint)Pointers.WowObject.DataPTR));
            LocalPlayer.CurrentHealth = WowReader.ReadUInt((LocalPlayer.UnitFieldsAddress + (uint)Descriptors.WoWUnitFields.Health * 4));
            LocalPlayer.MaxHealth = WowReader.ReadUInt((LocalPlayer.UnitFieldsAddress + (uint)Descriptors.WoWUnitFields.MaxHealth * 4));
            LocalPlayer.Level = WowReader.ReadUInt((LocalPlayer.UnitFieldsAddress + (uint)Descriptors.WoWUnitFields.Level * 4));
            LocalPlayer.Name = PlayerNameFromGuid(LocalPlayer.Guid);
            

            return LocalPlayer;
        }

        public WowObject getLocalTarget()
        {

            
            LocalTarget.Guid = WowReader.ReadUInt64((CurrentTargetGUID));
            // if we actually have a target
            if (LocalTarget.Guid != 0)
            {
                LocalTarget.BaseAddress = GetObjectBaseByGuid(LocalTarget.Guid);
                LocalTarget.XPos = WowReader.ReadFloat((LocalTarget.BaseAddress + (uint)Pointers.WowObject.X)); 
                LocalTarget.YPos = WowReader.ReadFloat((LocalTarget.BaseAddress + (uint)Pointers.WowObject.Y)); 
                LocalTarget.ZPos = WowReader.ReadFloat((LocalTarget.BaseAddress + (uint)Pointers.WowObject.Z));
                LocalTarget.Facing = WowReader.ReadFloat((LocalTarget.BaseAddress + (uint)Pointers.WowObject.FacingAngle));
                LocalTarget.Type = (short)WowReader.ReadUInt((LocalTarget.BaseAddress + (uint)Pointers.WowObject.Type));
                LocalTarget.Rotation = WowReader.ReadFloat((LocalTarget.BaseAddress + (uint)Pointers.WowObject.RotationOffset));
                LocalTarget.UnitFieldsAddress = WowReader.ReadUInt((LocalTarget.BaseAddress + (uint)Pointers.WowObject.DataPTR));
                LocalTarget.CurrentHealth = WowReader.ReadUInt((LocalTarget.UnitFieldsAddress + (uint)Descriptors.WoWUnitFields.Health * 4));
                LocalTarget.MaxHealth = WowReader.ReadUInt((LocalTarget.UnitFieldsAddress + (uint)Descriptors.WoWUnitFields.MaxHealth * 4));
                LocalTarget.Level = WowReader.ReadUInt((LocalTarget.UnitFieldsAddress + (uint)Descriptors.WoWUnitFields.Level * 4));
                LocalTarget.SummonedBy = WowReader.ReadUInt64((LocalTarget.UnitFieldsAddress + (uint)Descriptors.WoWUnitFields.SummonedBy * 4));

                // get the name of our target
                if (LocalTarget.Type == 3) // not a human player
                    LocalTarget.Name = MobNameFromGuid(LocalTarget.Guid);
                if (LocalTarget.Type == 4) // a human player
                    LocalTarget.Name = PlayerNameFromGuid(LocalTarget.Guid);


                // return target
                return LocalTarget;
            }
            else
            {
                return null;
            }
        }

        public ListWoWObjects getAllObjects()
        {

            // set our counter variable to 0 so we can begin counting the objects
            TotalWowObjects = 0;
            CurrentObject = new WowObject();
            // set our current object as the first in the object manager
            CurrentObject.BaseAddress = FirstObject;

            ListWoWObjects list = new ListWoWObjects();

            // set our current object as the first in the object manager
            CurrentObject.BaseAddress = FirstObject;




            // read the object manager from first object to last.
            while (CurrentObject.BaseAddress != 0 && CurrentObject.BaseAddress % 2 == 0)
            {
                // add to our counter
                TotalWowObjects = TotalWowObjects + 1;

                // type independent informations
                CurrentObject.Guid = WowReader.ReadUInt64((CurrentObject.BaseAddress + (uint)Pointers.WowObject.Guid));
                CurrentObject.Type = (short)(WowReader.ReadUInt((CurrentObject.BaseAddress + (uint)Pointers.WowObject.Type)));

               
                    switch (CurrentObject.Type)
                    {
                        case (short)Constants.ObjType.OT_UNIT: // an npc
                            CurrentObject.UnitFieldsAddress = WowReader.ReadUInt((CurrentObject.BaseAddress + (uint)Pointers.WowObject.DataPTR));
                            CurrentObject.CurrentHealth = WowReader.ReadUInt((CurrentObject.UnitFieldsAddress + (uint)Descriptors.WoWUnitFields.Health * 4));
                            CurrentObject.MaxHealth = WowReader.ReadUInt((CurrentObject.UnitFieldsAddress + (uint)Descriptors.WoWUnitFields.MaxHealth * 4));
                            CurrentObject.SummonedBy = WowReader.ReadUInt64((CurrentObject.UnitFieldsAddress + (uint)Descriptors.WoWUnitFields.SummonedBy * 4));

                            CurrentObject.XPos = WowReader.ReadFloat((CurrentObject.BaseAddress + (uint)Pointers.WowObject.X)); 
                            CurrentObject.YPos = WowReader.ReadFloat((CurrentObject.BaseAddress + (uint)Pointers.WowObject.Y)); 
                            CurrentObject.ZPos = WowReader.ReadFloat((CurrentObject.BaseAddress + (uint)Pointers.WowObject.Z));
                            CurrentObject.Facing = WowReader.ReadFloat((CurrentObject.BaseAddress + (uint)Pointers.WowObject.FacingAngle));
                            CurrentObject.Rotation = WowReader.ReadFloat((CurrentObject.BaseAddress + (uint)Pointers.WowObject.RotationOffset)); 
                            CurrentObject.Name = MobNameFromBaseAddr(CurrentObject.BaseAddress);


                            break;
                        case (short)Constants.ObjType.OT_PLAYER: // a player
                            CurrentObject.UnitFieldsAddress = WowReader.ReadUInt((CurrentObject.BaseAddress + (uint)Pointers.WowObject.DataPTR));
                            CurrentObject.CurrentHealth = WowReader.ReadUInt((CurrentObject.UnitFieldsAddress + (uint)Descriptors.WoWUnitFields.Health * 4));
                            CurrentObject.MaxHealth = WowReader.ReadUInt((CurrentObject.UnitFieldsAddress + (uint)Descriptors.WoWUnitFields.MaxHealth * 4));

                            CurrentObject.XPos = WowReader.ReadFloat((CurrentObject.BaseAddress + (uint)Pointers.WowObject.X)); 
                            CurrentObject.YPos = WowReader.ReadFloat((CurrentObject.BaseAddress + (uint)Pointers.WowObject.Y)); 
                            CurrentObject.ZPos = WowReader.ReadFloat((CurrentObject.BaseAddress + (uint)Pointers.WowObject.Z));
                            CurrentObject.Facing = WowReader.ReadFloat((CurrentObject.BaseAddress + (uint)Pointers.WowObject.FacingAngle)); 
                            CurrentObject.Rotation = WowReader.ReadFloat((CurrentObject.BaseAddress + (uint)Pointers.WowObject.RotationOffset)); 
                            CurrentObject.Name = PlayerNameFromGuid(CurrentObject.Guid);
                            break;

                        case (short)Constants.ObjType.OT_GAMEOBJ:
                            CurrentObject.XPos = WowReader.ReadFloat((CurrentObject.BaseAddress + (uint)Pointers.WowObject.GameObjectX));
                            CurrentObject.YPos = WowReader.ReadFloat((CurrentObject.BaseAddress + (uint)Pointers.WowObject.GameObjectY));
                            CurrentObject.ZPos = WowReader.ReadFloat((CurrentObject.BaseAddress + (uint)Pointers.WowObject.GameObjectZ));
                            CurrentObject.Facing = WowReader.ReadFloat((CurrentObject.BaseAddress + (uint)Pointers.WowObject.FacingAngle));
                            CurrentObject.UnitFieldsAddress = WowReader.ReadUInt((CurrentObject.BaseAddress + (uint)Pointers.WowObject.DataPTR));
                            CurrentObject.Name = ItemNameFromBaseAddr(CurrentObject.BaseAddress);
                            CurrentObject.GameObjectType = ItemTypeFromBaseAddr(CurrentObject.BaseAddress);
                            
                            break;

                    }
                

                // set the current object as the next object in the object manager
                WowObject tmpObject = CurrentObject;
                CurrentObject = new WowObject();
                list.Add(tmpObject);
                CurrentObject.BaseAddress = WowReader.ReadUInt((tmpObject.BaseAddress + (uint)Pointers.ObjectManager.NextObject));


            }
            return list;
        }

        public String getClientConnectionAddress()
        {
            return "Client Connection: " + ToHexString(ClientConnection);
        }

        public String getObjectManagerAddress()
        {
            return "Object Manager: " + ToHexString(ClientConnection);
        }

        public String getFirstObjectAddress()
        {
            return "First Object: " + ToHexString(FirstObject);
        }

        private String ToHexString(uint HexToConvert)
        {
            return "0x" + HexToConvert.ToString("X");
        }

        public Boolean teleportTo(float X, float Y, float Z) {

            WowReader.WriteFloat(((uint)LocalPlayer.BaseAddress + (uint)Pointers.WowObject.X), X);
            WowReader.WriteFloat(((uint)LocalPlayer.BaseAddress + (uint)Pointers.WowObject.Y), Y);
            WowReader.WriteFloat(((uint)LocalPlayer.BaseAddress + (uint)Pointers.WowObject.Z), Z);

            return true;

        }

        public float changePlayerSpeed(float speedval) {
            float speed = WowReader.ReadFloat(((uint)LocalPlayer.BaseAddress + (uint)Pointers.WowObject.Speed));
            WowReader.WriteFloat(((uint)LocalPlayer.BaseAddress + (uint)Pointers.WowObject.Speed), speedval);
            return speedval;
        }

        public float getPlayerSpeed()
        {
            float speed = WowReader.ReadFloat(((uint)LocalPlayer.BaseAddress + (uint)Pointers.WowObject.Speed));
            return speed;
        }

    }
}
